package org.ncibi.cytoscape.metscape.network;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.ncibi.cytoscape.metscape.data.Attributes;
import org.ncibi.cytoscape.metscape.data.Concept;
import org.ncibi.cytoscape.metscape.data.ConceptData;
import org.ncibi.cytoscape.metscape.plugin.MetScapePlugin;
import org.ncibi.cytoscape.metscape.ui.MouseClickHandler;
import org.ncibi.cytoscape.metscape.ui.PopupNodeContextMenuListener;
import org.ncibi.cytoscape.metscape.visual.VisualStyleFactory;
import org.ncibi.metab.network.MetabolicNetwork;
import org.ncibi.metab.network.attribute.CompoundAttribute;

import cytoscape.CyEdge;
import cytoscape.CyNetwork;
import cytoscape.CyNode;
import cytoscape.Cytoscape;
import cytoscape.data.CyAttributesUtils;
import cytoscape.data.CyAttributesUtils.AttributeType;
import cytoscape.groups.CyGroup;
import cytoscape.groups.CyGroupManager;
import cytoscape.layout.CyLayoutAlgorithm;
import cytoscape.layout.CyLayouts;
import cytoscape.layout.Tunable;
import cytoscape.view.CyNetworkView;
import cytoscape.visual.CalculatorCatalog;
import cytoscape.visual.VisualMappingManager;
import cytoscape.visual.VisualStyle;
import ding.view.DGraphView;

public class MetScapeNetworks {

	private MetScapeNetworks() {
	}
	
	public static void addToRootGraph(MetabolicNetwork sourceNetwork) {
		NullNetworkTranslator translator = new NullNetworkTranslator(sourceNetwork);
		translator.toCytoscapeNetwork();
	}

	public static CyNetwork buildNetwork(String networkName,
			MetabolicNetwork sourceNetwork, Collection<String> compoundIds, Collection<Integer> geneIds) {
		hideAttributes(networkName);
		NetworkTranslator translator = new DefaultNetworkTranslator(networkName,
				sourceNetwork, compoundIds, geneIds);
		CyNetwork network = translator.toCytoscapeNetwork();
		configureNetwork(network);
		return network;
	}
	
	public static CyNetwork buildSubnetwork(Collection<?> nodes, Collection<?> edges, 
			String networkName, CyNetwork network) {
		
		hideAttributes(networkName);
		ConceptData conceptData = MetScapePlugin.getPluginData().getConceptDataStore()
			.get(Attributes.network.getIntegerAttribute(network.getIdentifier(), "ConceptData"));
		CyNetwork subnetwork = Cytoscape.createNetwork(nodes, edges, networkName, network);
		for(Object node: subnetwork.nodesList()) {
			CyNode cyNode = (CyNode) node;
			Attributes.node.setAttribute(cyNode.getIdentifier(), "Category." + networkName,
					Attributes.node.getStringAttribute(cyNode.getIdentifier(), "Category." + network.getTitle()));
			Attributes.node.setAttribute(cyNode.getIdentifier(), "direction." + networkName,
					Attributes.node.getStringAttribute(cyNode.getIdentifier(), "direction." + network.getTitle()));
			Attributes.node.setAttribute(cyNode.getIdentifier(), "isSignificant." + networkName,
					Attributes.node.getBooleanAttribute(cyNode.getIdentifier(), "isSignificant." + network.getTitle()));
			Attributes.node.setAttribute(cyNode.getIdentifier(), "isExpansion." + networkName, false);
			Attributes.node.setAttribute(cyNode.getIdentifier(), "isExpansionSeed." + networkName, false);
			Attributes.node.setAttribute(cyNode.getIdentifier(), "isSubnetworkExpansionSeed." + networkName, false);
			List<Concept> concepts = conceptData.getConceptMap().get(cyNode.getIdentifier());
        	if(concepts != null)
            for(Concept concept: concepts) {
            	CyGroup group = CyGroupManager.findGroup(subnetwork.getIdentifier() + "-" + concept.getConceptName());
            	if(group == null) {
            		group = CyGroupManager.createGroup
            			(subnetwork.getIdentifier() + "-" + concept.getConceptName(), null, subnetwork);
            		CyNode groupNode = group.getGroupNode();
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.name", concept.getConceptName());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numUniqueGenes", concept.getNumUniqueGenes());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork", 0);
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.pvalue", concept.getPvalue());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.fdr", concept.getFdr());
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.direction", concept.getDirection());
            	}
            	if(Attributes.node.getStringAttribute(cyNode.getIdentifier(), "Type").equals("Gene")) {
            		CyNode groupNode = group.getGroupNode();
            		Integer numGenesInNetwork = Attributes.node.getIntegerAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork");
            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork", ++numGenesInNetwork);
            	}
            	group.addNode(cyNode);
            }
		}
		
		for(Object edge: subnetwork.edgesList()){
			CyEdge cyEdge = (CyEdge) edge;
			Attributes.edge.setAttribute(cyEdge.getIdentifier(), "isExpansion." + networkName, false);
		}
		
		CyAttributesUtils.copyAttributes(network.getIdentifier(), subnetwork.getIdentifier(),
				Cytoscape.getNetworkAttributes(), false);
		
		configureNetwork(subnetwork);
		return subnetwork;
	}
	
	private static void hideAttributes(String networkName) {
		Attributes.node.setUserVisible(CompoundAttribute.PUBCHEMCID.toAttributeName(), false);
		Attributes.node.setUserVisible(CompoundAttribute.BIOCYCID.toAttributeName(), false);
		Attributes.node.setUserVisible(CompoundAttribute.CHEBIID.toAttributeName(), false);
		Attributes.node.setUserVisible(CompoundAttribute.HMDBID.toAttributeName(), false);
		Attributes.node.setUserVisible("Concept.name", false);
		Attributes.node.setUserVisible("Concept.numUniqueGenes", false);
		Attributes.node.setUserVisible("Concept.numGenesInNetwork", false);
		Attributes.node.setUserVisible("Concept.oddsRatio", false);
		Attributes.node.setUserVisible("Concept.direction", false);
		Attributes.node.setUserVisible("Concept.pvalue", false);
		Attributes.node.setUserVisible("Concept.fdr", false);
		Attributes.node.setUserVisible("__groupIsLocal", false);
		Attributes.node.setUserVisible("Category." + networkName, false);
		Attributes.node.setUserVisible("direction." + networkName, false);
		Attributes.node.setUserVisible("isSignificant." + networkName, false);
		Attributes.node.setUserVisible("inExpansionFor." + networkName, false);
		Attributes.node.setUserVisible("isExpansion." + networkName, false);
		Attributes.node.setUserVisible("isExpansionSeed." + networkName, false);
		Attributes.node.setUserVisible("isSubnetworkExpansionSeed." + networkName, false);
		Attributes.edge.setUserVisible("Type", false);
		Attributes.edge.setUserVisible("direction", false);
		Attributes.edge.setUserVisible("isExpansion." + networkName, false);
		Attributes.edge.setUserVisible("inExpansionFor." + networkName, false);
		Attributes.network.setUserVisible("CompoundData", false);
		Attributes.network.setUserVisible("GeneData", false);
		Attributes.network.setUserVisible("ConceptData", false);
	}

	private static void configureNetwork(CyNetwork network) {
		Cytoscape.firePropertyChange(Cytoscape.NETWORK_MODIFIED, null, network);
		CyLayoutAlgorithm layout = CyLayouts.getLayout("force-directed");
		Tunable discrete = layout.getSettings().get("discrete");
		Tunable deterministic = layout.getSettings().get("deterministic");
		if(discrete != null) {
			discrete.setValue(true);
			layout.updateSettings();
		}
		else if(deterministic != null) {
			deterministic.setValue(true);
			layout.updateSettings();
		}
		CyNetworkView view = Cytoscape.getNetworkView(network
				.getIdentifier());
		VisualMappingManager manager = Cytoscape.getVisualMappingManager();
		CalculatorCatalog catalog = manager.getCalculatorCatalog();
		VisualStyle style = VisualStyleFactory.createStyle(network);
		if(catalog.getVisualStyle("MetScape:"+network.getIdentifier()) != null) {
			catalog.removeVisualStyle("MetScape:"+network.getIdentifier());
		}
		catalog.addVisualStyle(style);
		view.setVisualStyle(style.getName());
		manager.setVisualStyle(style);
		view
				.addNodeContextMenuListener(new PopupNodeContextMenuListener());
		((DGraphView) view).getCanvas().addMouseListener(new MouseClickHandler());
		view.redrawGraph(true, true);
	}


	public static CyNetwork expandInExistingNetwork(String cid,
			MetabolicNetwork sourceNetwork, CyNetwork network) {
		ExpandInExistingNetworkTranslator translator = new ExpandInExistingNetworkTranslator(
				cid, sourceNetwork, network);
		network = translator.toCytoscapeNetwork();
		Cytoscape.firePropertyChange(Cytoscape.NETWORK_MODIFIED, null, network);
		return network;
	}
	
	public static CyNetwork expandInSubnetwork(String cid, String subnetworkName, 
			MetabolicNetwork sourceNetwork, CyNetwork network) {
		hideAttributes(subnetworkName);
		ExpandInSubnetworkTranslator translator = new ExpandInSubnetworkTranslator(cid, subnetworkName, sourceNetwork, network);
		CyNetwork subnetwork = translator.toCytoscapeNetwork();
		configureNetwork(subnetwork);
		return subnetwork;
	}

	public static CyNetwork collapseNetwork(CyNetwork network, String cid) {
		if(Attributes.node.getBooleanAttribute(cid, "isSubnetworkExpansionSeed." + network.getTitle())) {
			restoreOriginalNetwork(network);
			return network;
		}

		List<String> expansionNodeIds = CyAttributesUtils.getIDListFromAttributeValue
			(AttributeType.NODE, "inExpansionFor." + network.getTitle(), cid);
		List<String> expansionEdgeIds = CyAttributesUtils.getIDListFromAttributeValue
			(AttributeType.EDGE, "inExpansionFor." + network.getTitle(), cid);
		
		for (String id : expansionNodeIds) {
			List<?> inExpansionFor = Attributes.node
					.getListAttribute(id, "inExpansionFor." + network.getTitle());
			inExpansionFor.remove(cid);
			if (inExpansionFor.isEmpty()) {
				CyNode cyNode = Cytoscape.getCyNode(id);
				network.hideNode(cyNode);
				if(cyNode.getGroups() != null)
				for(CyGroup group: new ArrayList<CyGroup>(cyNode.getGroups())) {
					group.removeNode(cyNode);
					if(group.getNodes().isEmpty())
						CyGroupManager.removeGroup(group);
				}
				Attributes.node.deleteAttribute(id, "inExpansionFor." + network.getTitle());
				Boolean isExpansionSeed = Attributes.node.getBooleanAttribute(id, "isExpansionSeed." + network.getTitle());
				if (isExpansionSeed) {
					collapseNetwork(network, id);
				}
			} else {
				Attributes.node.setListAttribute(id, "inExpansionFor." + network.getTitle(), inExpansionFor);
			}
		}
		
		for (String id : expansionEdgeIds) {
			List<?> inExpansionFor = Attributes.edge
					.getListAttribute(id, "inExpansionFor." + network.getTitle());
			inExpansionFor.remove(cid);
			if (inExpansionFor.isEmpty()) {
				Attributes.edge.deleteAttribute(id, "inExpansionFor." + network.getTitle());
				network.hideEdge(Cytoscape.getRootGraph().getEdge(id));
			} else {
				Attributes.edge.setListAttribute(id, "inExpansionFor." + network.getTitle(), inExpansionFor);
			}
		}
		Attributes.node.setAttribute(cid, "isExpansionSeed." + network.getTitle(), false);
		
		Cytoscape.firePropertyChange(Cytoscape.NETWORK_MODIFIED, null, network);
		CyNetworkView view = Cytoscape.getNetworkView(network.getIdentifier());
		view.redrawGraph(true, true);
		
		return network;
	}

	public static CyNetwork restoreOriginalNetwork(CyNetwork network) {
		for(Object node: network.nodesList()) {
			CyNode cyNode = (CyNode) node;
			
			if(Attributes.node.hasAttribute
					(cyNode.getIdentifier(),"inExpansionFor." + network.getTitle())) {
				network.hideNode(cyNode);
				if(cyNode.getGroups() != null)
				for(CyGroup group: new ArrayList<CyGroup>(cyNode.getGroups())) {
					group.removeNode(cyNode);
					if(group.getNodes().isEmpty())
						CyGroupManager.removeGroup(group);
				}
			}
			
			if(Attributes.node.hasAttribute
					(cyNode.getIdentifier(),"isExpansionSeed." + network.getTitle()) &&
				!Attributes.node.getBooleanAttribute
					(cyNode.getIdentifier(), "isSubnetworkExpansionSeed." + network.getTitle())) {
				Attributes.node.setAttribute(cyNode.getIdentifier(), "isExpansionSeed." + network.getTitle(), false);
			}
		}
		
		for(Object edge: network.edgesList()) {
			CyEdge cyEdge = (CyEdge) edge;
			if(Attributes.edge.hasAttribute
					(cyEdge.getIdentifier(),"inExpansionFor." + network.getTitle()))
				network.hideEdge(cyEdge);
		}
		
		Cytoscape.firePropertyChange(Cytoscape.NETWORK_MODIFIED, null, network);
		CyNetworkView view = Cytoscape.getNetworkView(network.getIdentifier());
		view.redrawGraph(true, true);
		
		return network;
	}

}
